using System;
using AndyPike.Snowflake.Core.Exceptions;
using AndyPike.Snowflake.Core.Messages;
using AndyPike.Snowflake.Core.Queries.Accounts;
using AndyPike.Snowflake.Entities;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

namespace AndyPike.Snowflake.Core.Tests.Unit.Services.AccountServiceFixtures
{
    [TestFixture]
    public class When_registering_a_new_account : AccountServiceFixtureBase
    {
        [Test]
        public void Should_validate_the_registration()
        {
            var registration = mockery.DynamicMock<RegistrationMessage>();

            With.Mocks(mockery)
                .Expecting(delegate
                               {
                                   Expect.Call(registration.Validate);
                                   SetupResult.For(service.RegistrationToAccountMapper.Map(registration)).Return(new Account());
                               })
                .Verify(() => service.RegisterAccount(registration));
        }

        [Test]
        public void Should_leverage_the_repository_to_check_that_the_user_name_is_unique()
        {
            var registration = mockery.DynamicMock<RegistrationMessage>();
            
            With.Mocks(mockery)
                .Expecting(delegate
                               {
                                   Expect.Call(accountRepository.FindOne(Arg<AccountByUserName>.Is.TypeOf)).Return(null);
                                   SetupResult.For(service.RegistrationToAccountMapper.Map(registration)).Return(new Account());
                               })
                .Verify(() => service.RegisterAccount(registration));
        }

        [Test]
        [ExpectedException(typeof(DuplicateUserNameException))]
        public void Should_throw_an_exception_if_the_user_name_is_not_unique()
        {
            var registration = mockery.DynamicMock<RegistrationMessage>();

            With.Mocks(mockery)
                .Expecting(delegate
                               {
                                   Expect.Call(accountRepository.FindOne(Arg<AccountByUserName>.Is.TypeOf)).Return(new Account());
                                   SetupResult.For(service.RegistrationToAccountMapper.Map(registration)).Return(new Account());
                               })
                .Verify(() => service.RegisterAccount(registration));
        }

        [Test]
        public void Should_map_the_registration_dto_to_an_account_domain_entity()
        {
            var registration = mockery.DynamicMock<RegistrationMessage>();
            var account = new Account();

            With.Mocks(mockery)
                .Expecting(() => Expect.Call(service.RegistrationToAccountMapper.Map(registration)).Return(account))
                .Verify(() => service.RegisterAccount(registration));
        }

        [Test]
        public void Should_leverage_the_repository_to_save_the_account()
        {
            var registration = mockery.DynamicMock<RegistrationMessage>();
            var account = new Account();

            With.Mocks(mockery)
                .Expecting(delegate 
                               {
                                   SetupResult.For(service.RegistrationToAccountMapper.Map(registration)).Return(account);
                                   Expect.Call(() => accountRepository.Save(account));
                               })
                .Verify(() => service.RegisterAccount(registration));
        }

        [Test]
        public void Should_return_the_persisted_account_id()
        {
            Guid returnedAccountId = Guid.Empty;           
            var registration = mockery.DynamicMock<RegistrationMessage>();
            var account = new Account {Id = Guid.NewGuid()};

            With.Mocks(mockery)
                .Expecting(() => SetupResult.For(service.RegistrationToAccountMapper.Map(registration)).Return(account))
                .Verify(() => returnedAccountId = service.RegisterAccount(registration));

            Assert.That(returnedAccountId, Is.EqualTo(account.Id));
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Should_throw_an_exception_of_the_message_is_null()
        {
            service.RegisterAccount(null);
        }
    }
}